package org.fhm.zdte.common.service.servlet;

import org.fhm.substrate.annotation.BeanInitial;
import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.annotation.Setup;
import org.fhm.zdte.common.annotation.MapperParameter;
import org.fhm.zdte.common.config.ServletConfiguration;
import org.fhm.zdte.common.exception.ServletException;
import org.fhm.zdte.common.pojo.ZDTEFile;
import org.fhm.zdte.common.standard.IParametersAnnotationMatch;
import org.fhm.zdte.common.util.CommonUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * The Parameter-Value-Matcher {@link MapperParameter}'s implementation.
 *
 * @author 谭波
 * @since 2024/3/22
 */
@Component("MapperParameter")
public class MapperParameterMatch implements IParametersAnnotationMatch {

    private String tempFilePath;

    @Setup
    private ServletConfiguration servletConfiguration;

    @Setup
    private CommonUtil commonUtil;

    @Override
    public void isValidForParameter(Parameter parameter) throws ServletException {
        Class<?> clazz = parameter.getType();
        if (clazz.isArray())
            clazz = clazz.getComponentType();
        if (
                !clazz.isPrimitive()
                        && !String.class.isAssignableFrom(clazz)
                        && !Number.class.isAssignableFrom(clazz)
                        && !Boolean.class.isAssignableFrom(clazz)
                        && !Character.class.isAssignableFrom(clazz)
                        && !File.class.isAssignableFrom(clazz)
        )
            throw new ServletException("the annotation @MapperParameter can only be used to decorate string and primitive type.");
    }

    @Override
    public void match(Map<String, Object> parametersMap, List<Object> parametersList, Parameter parameter) throws ServletException {
        String parameterName = parameter.getAnnotation(MapperParameter.class).value();
        Object parameterValue = parametersMap.get(parameterName);
        if (Objects.isNull(parameterValue))
            throw new ServletException("the parameter names do not match");
        Class<?> parameterType = parameter.getType();
        if (parameterValue instanceof ZDTEFile) {
            ZDTEFile zdteFile = (ZDTEFile) parameterValue;
            if (parameterType.isAssignableFrom(File.class)) {
                File tempFIle = new File(tempFilePath + commonUtil.getCurTimeMillisSecond() + "_" + zdteFile.getFileName());
                try (FileOutputStream fos = new FileOutputStream(tempFIle)) {
                    fos.write(Base64.getDecoder().decode(zdteFile.getFileData()));
                } catch (Exception e) {
                    throw new ServletException("file conversion failed", e);
                }
                parametersList.add(tempFIle);
            } else {
                throw new ServletException("if the conversion of the file object fails, check the corresponding servlet parameter type");
            }
        } else {
            try {
                Object o;
                if (parameterType.isArray() && parameterValue instanceof Collection) {
                    Collection<?> collection = (Collection<?>) parameterValue;
                    o = Array.newInstance(parameterType, collection.size());
                    int i = 0;
                    for (Object object : collection) {
                        Array.set(o, i, object);
                        i++;
                    }
                } else {
                    o = parameterType.cast(parameterValue);
                }
                parametersList.add(o);
            } catch (Exception e) {
                throw new ServletException("the type of parameter " + parameterName + " does not match");
            }
        }

    }

    @BeanInitial
    private void beanInitial() throws ServletException {
        this.tempFilePath = servletConfiguration.getTempFilePath();
        File file = new File(tempFilePath);
        if (!file.exists()) {
            if (!file.mkdirs())
                throw new ServletException("failed to save the temporary file, please check it and try again");
        } else {
            if (!file.isDirectory()) {
                throw new ServletException("the configured temporary file path is not a directory, please check it and try again");
            }
        }
        if (!tempFilePath.substring(tempFilePath.length() - 1).equals(File.separator)) {
            this.tempFilePath += File.separator;
        }
    }
}
